Hello everyone, my name is Chris and I’m Fun Freighter’s programmer. We recently released an asset called Decal King, which is a tool for adding decals to surfaces. Currently in Unity not all render pipelines support the typical method of adding decals (projectors), so our asset uses another method. The intended sales pitch was something to the effect of “it works regardless of render pipeline” but in the end I discovered to my pleasant surprise its likely more performative than projectors in most respects.
First, a quick summary of what Decal King actually is. At its core, it’s a script that takes an image (in the form of a standard material like any mesh would have), creates a mesh for it, then tests each vertex of the new mesh to find what surface is “beneath it” by raycasting against colliders. The result of this is a “mesh decal” that is literally just mesh hovering right above the surface its on top of, obscuring the mesh underneath it.
This is where the original intended sales pitch comes in: because it uses a generic material, it can work with any possible render pipeline you could come up with for Unity. It also means, in theory, a user could do some weird stuff with the decals. For example a decal that displays the skybox behind it, or a transparent decal, or a decal that displays the output of a camera. Those are just examples; basically anything you could normally do with a material, you can do with Decal King.
I talked earlier about unexpected performance gains compared to projectors. Projectors are handled via shaders and thus handled via the GPU. The GPU doesn’t like branching code. If there’s a condition (like say, a projected image being present) it will always resolve the condition as true and as false, paying for both branches but only using the output of one. So any pixel that could have an image projected on it, the GPU is doing extra work. It might not be a lot, but the average monitor has many pixels and it adds up. Decal King in theory should be a trade off, because on one hand most frames it has no performance impact at all, but on the other hand when a decal is created a large number of raycasts have to be sent out. So it trades spikes in CPU usage in the place of a constant increase in GPU usage, which on paper might not be a good trade. But, perhaps because all the raycasts are short, the CPU usage is almost completely negligible. It’s actually not much of a trade off at all, so I’m very happy with how that ended up. To provide an example, most first person shooters start deleting bullet holes when somewhere between 10-30 exist. Decal King supports doing that but as far as I can tell the user could make a thousand bullet holes and as long as they weren’t created all at once, there would be no slowdown. The system does have a couple glaring weaknesses compared to projectors, which I’ll get into later on.
For me the biggest surprise of this process was the timeline of producing the asset. I had an idea for a way to create decals based on code I’d already written for another project, and so I threw a prototype together in a couple days. It mostly worked as intended, it seemed like this would be a quick digression and then we would be done.
At the time I hadn’t produced anything for the asset store, any readers who have probably know where this is going. I’m used to writing code to be used “in house,” meaning I know exactly how its going to be used and, for a small game company like us, if anyone wants changes to the code or is confused about it I can just talk to them. The asset store is different, because I don’t know how people will use my code, and I can’t assume that they will interpret any instructions I write “correctly.” I put correctly in air-quotes because of course just because I mean a statement one way, the words I use to write it might be ambiguous. I’m not a professional tech writer and we’re way too small to hire one, so I can’t be sure all of my instructions are perfect. The bigger problem is in the year 2020, you can’t assume people are going to read the manual for any product, electronic or otherwise. So knowing all this, I tried to design a product that was as usable as possible. I’m fairly proud of how that turned out.
For purposes of usability, I liked to think in the case of a “worst-case user.” That is, a perfect storm of everything that could cause issues we anticipate: the user is inexperienced with Unity, the user doesn’t pay full attention to the store page, they use the asset to fulfill a purpose I didn’t anticipate or in way I didn’t expect, etc. The reason I think like that is that I feel, as content creator, user experience is my responsibility not my customer’s responsibility. Out of everyone that uses Decal King, there’s going to be one person that has the best experience and one person that has the worst experience. If that person who has the worst experience can still get the product to work, then we’ve done our job perfectly.
This is where the majority of my time was ultimately spent. We decided early on in the process that while we might use scripts for our demos, the actual asset itself would take the form of a single C# script. We might attach that script to prefabs that have already-reasonable settings, but in the end the user should be able to use that one script and nothing else and it will let them use Decal King immediately. I stand by this decision; I personally would hate to open up an asset from the asset store and then have the readme tell me I have to drag this this and that into the scene and follow a whole process to set it up. Where we needed new objects and components, the script creates them. For example, for reasons of controlling rotation the actual decal mesh should be parented to another game object that the user can rotate. So what the script does is it creates a child game object and then attaches all the necessary components for the decal mesh to it.
The thing is that this one script has to cover a lot of ground, while still appearing simple to use. Its supposed to encompass static decals, moving decals, creating static decals when a condition is met, decals that have random rotation or use a random material, and other modifiers still. So as you might expect, once I was mostly done with the functionality, the script was a huge list of variables. So I set about categorizing them. As far as I know there’s no way to tell Unity directly to put variables in a drop down list, so I imitated that behavior by using serializable classes to set up groups of variables. I split these broadly into three categories, one of which is basic and two of which are advanced. The idea is the user doesn’t actually have to open the advanced options, they can just click on the first set of variables and get what they want. So having now effectively made the list of variables “shorter” (by hiding ones that would be used rarely) I set about categorizing them. This involved picking self-explanatory names, adding tooltips, and writing a very detailed readme that covered every variable individually.
Then of course I had to try to break my asset by using it in every use case I could think of. What if the decal ends up cut into multiple pieces? What if they try to cast a decal onto a curved surface, or one that is very close to another surface. And just combining the options in their various combinations to see what would happen. What I learned from this process: one person does not a QA team make, especially if they created the product. All of the greatest insights I learned about my own script happened when I handed it over to Calvin with minimal instruction and he told me what happened when he tried to use it. I often found that I had been focusing deeply on issues that didn’t matter and was unaware of glaring flaws. I have no idea how I could have produced a polished product alone. It certainly would have taken longer.
This is a simple problem but as any programmer can attest simple problems can still cost time, and in this case we had a versioning issue that cost us about a week. The problem is simple, and obvious, but still an issue. Basically, I did my work on Decal King in the most recent version of Unity, like I do most of my work. As a coder I’m used to scripts being fairly version-independent. There are exceptions, but in general I’ve found a C# script or 3D model that can be imported into Unity 2019 can be imported in Unity 2017, 5.x, or even earlier versions. However, when we mapped out our design process, we put “test package in different versions of Unity” at the end of our process. So we didn’t realize what is very obvious in hindsight: Unity versions are backwards compatible, not forwards compatible. You can drag a project from 2017 to 2019 and it will work fine, but go even one version back and your project is likely to start throwing wild errors. For a few days I tried the very frustrating process of troubleshooting those errors, or trying different methods of importing the package into earlier versions, before taking a step back and realizing the problem. We also didn’t know, that Unity has a minimum version for asset store uploads. You can’t go submit for any version older than that (at the time we submitted Decal King, it was I believe 2018.4). So, lesson learned: always find out the earliest version you can submit an asset at, and work in that version. The user should have no problems importing into the most recent version of Unity. What we ended up doing was taking all the component scripts and other assets that made up our package, importing them separately into the minimum version, and then re-creating our demo scene. It was obnoxious but not a serious issue in the end.
This is a general problem with planning, basically since I underestimated the commitment of this project I didn’t do enough research into our competition. As a result we picked a price point that was too high relative to other, vaguely similar assets to start off with. As a result initially we only made one sale, although we since reduced the price to $25 and almost immediately made an additional sale (at time of writing the price has only been changed for a couple days). I’m fully optimistic we’ll make more sales, but it is unfortunate that by setting the price too high as a start, we’ve not gained too much information about how much money we can expect from asset store sales. It was never meant to be a primary income source for us but I am curious to what extent the asset store can supplement our income and justify time spent.
All that being said, I don’t regret that decision. People need to be paid for their work. This is getting a little philosophical, but I feel people in online markets tend to find themselves in a race to the bottom price wise that doesn’t help anyone. It helps the consumer in the short term but eventually providers are pushed out of the market and the product the end user receives suffers as competition stagnates, workers and sellers leave for more profitable markets, and less man-hours overall get dedicated to producing whatever it is the market sells. My personal aim in breaking into the asset store was never to immediately start pricing the competition out. I’m sure if we sold Decal King for $4.99 we’d have many sales. We might even have sold enough to make up the difference in individual revenue each sale brings in, but in the end we would have been selling ourselves and the competition short. Much like how artists should never work for exposure, and no one should ever do an unpaid internship, I don’t think anyone should try to sell a dirt-cheap product. It’s a bad attitude, never work for free (or almost free). For these reasons I wouldn’t expect the price to go any lower.
Most programmers, at least most game developers, that I know generally tend to cast too wide or too vague a net on scope. I’m happy to say here that I actually cast my scope too narrowly. The main way that I did this, which is honestly my main regret on the project, relates to rigged models. So basically, because the script creates mesh that “hugs” the terrain, and relies on an underlying collider, my assumption going in was that it simply wouldn’t work with rigged an animated models because their colliders wouldn’t match up with the mesh, and even they did, it would be difficult or impossible to get the mesh decal to follow the skin of the model in a way that looks convincing. There’s only one use case where this could possibly matter in my opinion, which is blood on a humanoid or animal model that’s been attacked. But this is a common use case and it would have been nice to cover it. We actually considered having a demo where the player could shoot humanoid target dummies, and if they missed a bullet hole would appear on the wall, but if they hit a dummy blood would appear on the wall behind them. It was a good idea but we decided against to avoid consumer confusion: if we showed bloodstains on a wall, people might think they could put bloodstains on a character’s arm, and I can’t honestly say that that’s true.
Where my regret comes in is that I never actually checked on this. For all I know Decal King works perfectly on rigged models. It probably doesn’t, and I’d probably gain nothing by checking, but it’s a missed opportunity. I should have experimented with that. However, there’s an even bigger regret I have: I wish I had spent some time and looked up how projectors worked, so I could have added a projector mode. Including a backup way of making decals would have been perhaps an impractical use of my time since it would be designing two parallel systems that do the same thing. However, since it would be making decals the “normal” way, I assume it would work in all situations decals are used int, including rigged models. Additionally, as long as they all work, more options pretty much just makes a better product. If I had another few weeks to work on Decal King, that’s what I would work on, no hesitation. But at some point you have to ship the product and there wasn’t time to greatly increase the scope by adding a whole second system for making decals.
Our collaboration issues were arguably a second instance of me setting my scope too narrow. At the start of the project I created a basic demo scene where the user would have a target indicator, and then when they clicked a laser (represented by a line renderer) would appear and leave a scorch mark. I had to write and test a couple scripts for the controls and the “laser” itself. In the end, this was a complete waste of time. My demo was set against a backdrop of the default Unity terrain, which uses a checker pattern. I correctly identified that any decals I made myself would look bad so I had Calvin (our artist) make me decals. What I incorrectly decided, as part of my “this won’t take long” illusion, was that I should be working on a demo scene at all. The demo scene Calvin ended up coming up with, which was a concrete urban environment where the player could use a spray can to spray decals on the walls, was vastly superior to anything I could have created. Calvin occasionally delves into coding or coding-esque features (shader graphs, basic C# scripts) but I would never expect him to be a primary coder. In the same way, as a programmer, I should have recognized that I was not going to be the primary person responsible for presentation. It was Calvin’s idea, not my own, to come up with the demo scene I mentioned earlier. But really I should have just asked him to make a demo from the beginning.
If I had a time machine, the end result would probably be shaving maybe two weeks off a five week project. And possibly using those two weeks to expand the scope, or just releasing Decal King. In the end I think we were pretty efficient, for the first time we did this.
It’s not necessarily the main lesson I learned from THIS project, although it certainly applies, but to anyone starting out coding and especially anyone trying to break into game development, all I can say is this: collaborate, collaborate, collaborate. All games need at a minimum art, coding, and sound, and one person just can’t do all 3. Not competitively. Without an artist working with me Decal King would have trailer, bad looking decals, a much inferior demo, and I would have been completely in the dark as to user experience. The only way I would have to know what my product was like would be to release it to the public and have them tell me the usability problems. But even adding just one person to a project creates so much value.