Steam Workshop with Unity and Facepunch Steamworks

Adding workshop functionality to a game is something I’ve wanted to learn to do for a long while. I dreamt of doing it with my first game and every project since then. There seemed like there were so many sticking points and potential problems. As I see it there are two main problems.

  1. Not only do you have to create the tools to make that content you have to create the framework to handle that content… While that may sound easy, I don’t think it is. At least for most games.

  2. The documentation on how to implement workshop functionality is scarce. Really scarce. At least from my searches. I’ve found almost NOTHING.

screenshot ac17b53e-8d59-45a3-8e54-410b276034a7.png

With Where’s My Lunch it was easy to figure out the type of content. Levels!

I already had a sandbox level built into the game, so turning that into a level editor really shouldn’t be too much of a stretch. In my earlier post, I explained how I’m using “Easy Save” to save all the data from levels and store it as an external file. It’s surprisingly simple… even easy. ;)

With the type of content and a simple (singular) external file the first problem is largely solved.

The second problem that of the lack of documentation… Was seemly solved by using Facepunch Steamworks. Well, sort of.

Facepunch, provides some code snippets of how to upload and update a workshop item. It looks pretty simple. And it is, sort of. As always the problems lie in the details. And those details can often depend on your projects needs and structure.

Disclaimer & Goals

The main goal of this post is to give an example of how I implemented the Steam Workshop and not to give a step by step process for you to follow exactly - I actually don’t think it’s even possible since every game is so different.

I’m going to try to talk about big ideas and point out the problems I had along the way. I’ll look at how to upload, download and update your workshop items.

These are things that any and all implementation of the Steam Workshop will need to do - at least I think so.

Now, I’m not going to look at how to handle the data and files that are uploading and downloading inside of your project as that’s almost 100% dependent on the type of files and how they’re being used in the individual project

I’m also sure there are some better ways to do what I’ve done. That’s just the way it is and I’m okay with that. If you know of a better or easier way, leave me a comment, I always love to learn something new!

One last thought. When it comes to topics like this… well… there is only so much hand holding that can be done. If you are just getting started with Unity and C#, to be honest, this probably isn’t something that you should be trying to do until you get more experience.

Setting Up the Backend

There’s a not that NEEDS to get set up in Steamworks, but there are a few things. And! They are not covered in the Facepunch Documentation.

Workshop.png
Enable UGC.png

First, we need to enable UGC file transfer, which is done with a single toggle.

Easy.

Workshop Visibility.png

Next, we need to set the visibility state. The default setting will be enough for you, the developer, to upload files, but if you want your audience to be able to test the workshop your going to need to do more work. For early testing I choose the second option, which requires a custom Steam group for your testers. Anyone in that group will automatically have access to the workshop. Which option you choose is, of course, up to you and your project’s needs.

THEN! There is one more important step.

If you are uploading images or larger files you need to enable Steam Cloud and increase the byte quota and number of files per user. In the Steam Developer forums the Valve employees seemed to be in favor of raising the limits far high than expected - which I imagine is to avoid errors and player frustration. I have no idea why the default is so low.

If you don’t change these settings your uploads will fail and there will be no indication that this is the problem. I didn’t hit this snag originally while uploading small save files, but once I added preview images the uploads started failing.

SteamCloudSettings.png

And I spent hours! Hours! Trying to figure out the problem. So yeah. Go change those settings before you go too far.

There are of course other settings but these are the basics that are required.

Uploading!

This is an exciting step. And not a hard one… If it works.

Facepunch and of Steamwork gives almost no indication for the cause of problems if there are any. So yeah. Find some patience and be prepared to spend some time searching for solutions.

The example given by Facepunch is a pretty good start. It’s easy, but with minimal feedback there are a few pitfalls.

Below you can see the upload function that I’m using. I’ve minimized lines that are overly specific to my project.

Workshop Upload.png

To upload you will need a path to the folder containing all of the files and assets. For the file preview image you will need a path to the actual file that will be uploaded.

ProgressClass.png

After that the code snippet from Facepunch shows what you want you need to do. There are several parameters or options for the upload, that while not well documented are named well enough to figure out most of them. For my purposes I added a description, a few tags and set the visibility to public. If you don’t set the visibility to public, the upload will succeed, but the it will be set to private by default.

You may also notice that I’ve created a new instance of the Progress Class. For the most part my version was taken straight from Facepunch, with the addition of a few clumsy bits that will provide some visual feedback to the user while their files are uploading.

The uploading process is asynchronous, so after the upload process has been attempted I send a message to the console based on the results. The results, don’t tell you much, beyond whether the upload was a success or not.

I really wish there were more clues to why an upload may have failed…

If the upload did fail, I display a message to the user and then invoke an event to make sure all the systems that might care about a failed upload know it happened.

It takes a few minutes, but assuming the upload is successful, your workshop item will appear on the game’s workshop page.

Pretty sweet and not too hard.

Downloading

The idea behind downloading is to do a search, then based on the results of that search individual workshop items can be queried or downloaded.

Once again, the Facepunch documentation is pretty good and the process of doing a search is fairly straightforward. In my code, I search by tag and then have other optional searches that can be added by the player.

The search also requires a page number. By default I get the first page, but it’s likely you will want additional pages and you will need to handle this in your implementation. In mine I repeat the search and increment the page number when the player scrolls to the bottom of the list.

Get Workshop Level List.png
Workshop Search Options.png

I choose to wrap the search options in a class for convenienceand to reduce the number of input parameters in the class. While I included many of the possible search parameters, I didn’t include all of them but this custom class will allow me to easily add new parameters without breaking the search functions.

Just like the upload process the search process is asynchronousand the results will come back in a short period of time so it must be done in an “async” function and wrapped in a try/catch.

It’s possible that the results are empty and Facepunch provides a “hasValues” property that can be used to ensure or check that the search was successful.

Do Search Function.png
Iterate Through Search Results.png

Then the results, if there are any, can be looped through with a foreach loop like so.

Displaying Workshop Item info

Displaying Workshop Item info

How exactly you handle those results is of course up to you. Steamworks.Ugc.Item type gives you access to the title, description, votes by the community, a url to the preview image and a whole lot more. Accessing these properties is straight forward, but once again the handling of those values is very much dependent on your project.

To the right (ish) you can see my user interface for each workshop level. The buttons on the bottom right are contextual and change visibility depending on the status of the item. There are also download and delete buttons that are currently hidden and will appear when they can be used.

The actual downloading of an item is quite simple and easy. Items are downloaded by Steam ID which is readily accessible from the workshop item. The files are downloaded to folder in a Steam library. There location can be found with the “directory” property of the Steamworks UGC Item.

Download Workshop Item .png

Do note that you are not downloading a Steamworks UGC Item type! You are downloading the same files you uploaded.

This caused some struggles on my end. It was easy for me to think I no longer needed reference to the Steamworks UGC Item and just work with the downloaded files. Once you lose reference to the item there is no (easy) way to find it again from the downloaded files. By losing reference to the item you lose access to lots of metadata that you’re probably going to want.

So tracking or keeping reference to an item is important and many of my functions pass references to items not the saved files. It’s okay if that doesn’t make sense… I think it will once you start implementing your own solution.

To Subscribe or Not To Subscribe?

So maybe I’ll show my ignorance with the Steam Workshop, but I was under the impression that I didn’t need or want to subscribe to each and every level that a user might want to try out. In the API downloading and subscribing are different actions. I couldn’t find anything that said you should do both…

So here’s me saying I think you should do both!

It keeps things simple and is one less “thing” that needs to get checked. There was some snag I hit… to be honest I can’t remember what exactly it was now, but it was going to take a lot of work engineer around not subscribing. So yeah. Just do it. It’s easy and personally I don’t see a downside for the player.

Updating Workshop Items

The last big hurdle with the workshop was updating items... Once again, the actual updating is pretty straight forward and is very similar to the uploading. The biggest difference here is that rather than create a new community file we are passing in the Steam ID of the item which allows Steamworks to update the files.

Update Workshop Item.png

The one big snag I hit was that the update will fail IF the file has not changed. There’s no indication that this is the problem, the files just won’t upload or update. Which makes sense but leads me to the next issue…

In WML players can locally save a level and don’t have to upload it. This makes sense to me on a lot of levels and I’d venture a guess it’s how most games do it too. But this means that there could be a local version and the downloaded workshop version in different folders… On top of that there’s no easy way to compare those files or know if one exists and the other doesn’t. It seemed to get messy in a hurry.

So if a player makes changes to a level, which version should it save to?

Hmm. Maybe this is obvious, but I definitely needed to think about it for a good while.

I came to the conclusion that changes should always be made to the local versions. And those local versions would need to get pushed to the workshop. This means if a user downloads someone else’s item then before they can edit it a local version is saved.

Check Ownership.png

It’s also unclear to me whether Steam itself checks ownership, so I created an internal check of the item ownership before updating. If the original item is not owned by the current user the files are uploaded to the workshop as a new item. If the original is owned by the player the files update. This leaves out the edge case of the owner wanting to upload the files as a new item, but I’m okay with that.

Deleting Items

Delete Workshop Item.png

It’s quite possible that users will want to delete an item that they’ve uploaded - this is especially true if you are doing some testing with the workshop. And it’s once again very easy. One function call with the Steam ID and it’ll get removed.

The process does take some time and could cause some issues if the user refreshes the search as the item seems to be partially still there for up to a minute or two. For WML, I have the imperfect solution of turning off the UI object when a level is deleted. This gives the user some indication that the deletion is happening, but if they refresh the search I don’t have a system in place (yet) to not show the partial and confusing results.

Conclusion

In the big scheme of things that’s really not that complicated. The amount of code needed to upload, download and update files is actually quite small. The bulk of my code is handling the UI or controlling the input and output of these functions - I’m happy to share those bits, but they are highly dependenton the game and I’m not sure they are particularly useful. But I could be wrong.

Game Jam... Now What?

When you finish a game jam you’re exhausted, but hopefully you’re proud of what you’ve made. Or better yet proud of what you’ve learned.

And that’s what game jams should really be about. Learning something new. Pushing yourself to create new systems or learn a new technique. And we should celebrate what we’ve learned and the new thing that we created. This is so much more important than how well you may have done compared to others. 

GMTKJame.png

Certainly, measuring yourself against others has its place, I won’t say it doesn’t - I race bikes for fun, but that shouldn’t be your sole focus or measure of your success when taking part of a game jam. 

Which is great and all, but that’s not really what this is all about. I’m not here to give a pep talk. I want to talk about what to do with your game after the jam is over. Should it just gather digital dust never to be played again? Well, maybe. 

But! What if there’s more to learn? What if there’s more to do with that game?

AND THAT! 

That chance - that there’s more to learn and more to do is what this series of videos and blog posts is going to be all about. 

So let’s rewind a bit.

WML.png

This summer, for the Game Maker’s Toolkit game jam I made the game “Where’s my lunch?” It was an okay game. Nothing profoundly creative or groundbreaking - just okay.

And I like many others I planned on leaving the game in a folder and never really looking at it again. 

And then I got a suggestion from a viewer: Why not take that game and publish it? The process could make great video content and be a resource for others who might want to do more with their project. 

Whoa! 

I let the idea rattle around in my head for a few days - then a few weeks. The more I thought about it the more I liked it. 

Over the last year or two I’ve been far more focused on making video content rather than working on my own project. Which means I often just have a few hours a month to work on the game. The progress is slow AND that progress is further slowed by needing to remember what I was doing two weeks ago or what problem I was trying to solve last month. 

It’s not working great. And I suspect some of you know exactly what I’m talking about. It’s really really hard to find 10-20 hours a week to work on a project.

Now by their very nature game jam games are simple. Their scope is small - even tiny - and that makes it perfect for me, and maybe you.  

Game Jam to Steam (GJTS)

We can all dream… right? :)

We can all dream… right? :)

So maybe, just maybe… I can work on a game AND create decent tutorial content for the channel at the same time!

Now, this project isn’t going to make me piles of money - I don’t think that’s even a distance possibility and that’s really not the goal. The goal is to make something, finish that something...  AND! Document the process so that others, maybe you, might do the same thing with your game.

How many of us have dreamt about publishing a game to steam? How many of us have a game from a game jam sitting in a folder that with a few months of polish could be worth sharing with a larger audience? 

Now, I’m not talking about some generic 2D platformer with floaty jump mechanics that was your first Unity project. But I’m also not saying that you need to place in the top 10 of an international game jam. I certainly didn’t!

Out of 5381 games… Not too bad. Not Amazing.

Out of 5381 games… Not too bad. Not Amazing.

Maybe your game needs some redesign or a few tweaks to be it’s best and that’s okay.  “Where’s My Lunch” certainly does. It did well on fun factor and a few other bits but clearly isn’t a particularly original game idea. 

And again. 

That’s okay. 

So let’s do it. Let’s do it together. Let’s take a game from a game jam, polish it and publish it!

Videos Incoming!

For the videos, I’ll be trying to find the sweet spot somewhere between a traditional dev log and a tutorial. All while bringing you along for the ride, showing you my progress, and hopefully helping you make progress on your own project. 

FacePunch.png

I’ll be adding the game to steam (It already is! Just not public.), making use of Facepunch Steamworks, adding Steam features such as achievements, maybe the Steam Workshop, and of course, polishing and expanding the gameplay to add dozens of levels - all hopefully without too much feature creep. 

I’m also looking into several of the features offered by Unity such as analytics and cloud diagnostics...

When there’s a system or process that can be of use to the larger game development community, I’ll slow down and do more of a complete tutorial on how the system works and how I built my version of that system.

Sound interesting? Sound useful? I hope so. 

So Let Me Introduce You…

Okay, doesn’t show much… This is the sandbox level

Okay, doesn’t show much… This is the sandbox level

“Where’s My Lunch” is a hand drawn 2D game that presents a new puzzle to be solved by the player at each level. The goal is to steer the character towards their lunch --- but your controls aren’t very precise.

The theme for the game jam was “out of control” and that’s reflected in some of the chaos of the game play. Your only tools, in the current build, are bombs, portals, and gravity wells. 

The bombs explode and exert a force. 

The portals come in a linked pair and warp the player’s from one location to another.

And the gravity wells, they exert an attractive force based on distance from the player.

Now I WILL be adding to these as I work on the game, but the three mechanics were enough - or in reality it was all I could manage in a 48-hour game jam.

There are of course some bugs that need to get fixed or addressed. And mostly this has to do with placing items.  At the moment it’s easy to place a portal on top of the player and then be unable to move it later. I also need to add a “clear level” functionality to let the player wipe the level and start over with a fresh attempt.

Other things like placing a gravity well near the sandwich seems to make it too easy, but more importantly, some of these flaws allow a player to “break” a level by finding a trivial solution - which is okay, but it can take some of the challenge and thus reward out of the game.

Clever level design is maybe the toughest challenge that I see in completing the game. Puzzle games, I think, are often seen as “easy” to make but the reality is they just as tough or tougher than other genres… But again I need to remind myself that the goal here isn’t to make the most awesome game just to make a decent game.

Down the Rabbit Hole!

Let’s dig in a bit into how things actually work in the game - the mechanics of the game so to speak. 

mw3E5Y.png

Here it’s pretty simple, no surprise, and the game leans heavily on the physics engine - which does have downsides as it’s not fully deterministic - and by that, I mean that two identical starting conditions won’t have identical results - they’ll be close but not perfect.

But that’s a battle for another day, and the built-in physics engine is plenty good enough for my project.

The player object is a ragdoll with a basic bone structure built from 2D hinges. There’s not really any gameplay mechanics here, it’s just a lot more fun to watch the player fly across the screen with the arms and legs flopping all over the place. 

Player.png

Since the physics engine is non-deterministic, I chose to use a large circle collider on the player body as this made the motion of the object more repeatable with just a small loss in floppy ragdoll goodness.

After that, the code is mostly about triggers and a little custom code for each of the tiles or objects in the scene. 

As a fun challenge during the game jam and at the suggestion of a viewer, I added a sandbox level that allows the player to create their own level from scratch. This is one of the systems that I plan on expanding so that players can not only build levels but share those levels! Hopefully using the Steam Workshop functions.

Maybe I’ll even be able to incorporate some player-made levels in the final build…?

During the game jam, I quickly realized that I needed to streamline the workflow for making and saving levels. Having each level in its own scene was going to get messy in a hurry! I needed a better way.

One of the great things about game jams is you are forced to get creative and do so efficiently and as a result, I came up with a simple but effective scene creation system. 

SaveManager.png

My system, which I’ll do a full tutorial on, essentially scans the scene in the editor for game tiles which all have a “save level object” component and then stores the basic transform information for each object as well as a reference to what type of object it is. The data is stored in a list on a scriptable object. 

Since the data is stored in a simple list, I’m hoping this will make using 3rd party tools like Easy Save quick and hopefully relatively painless. The plan is to have each level stored in a separate save file which can, hopefully, be shared between players on the Steam Workshop. I’ve never done any work with the Steam Workshop, so I’m pretty excited about that!

And that’s pretty much it. There are certainly other chunks of code, such as the code that allows the player to wrap around the screen or to trace the path of the player, but those are small details and not core functionality. If you want to see those bits, let me know in the comments below. 

What’s Next?

Where is the game going? What’s going to be added?

ToDoList.png

I’ve got a list of new mechanics that I want to add to the game. These include buttons, levers, doors, flame throwers, ice, electric fields, and whatever else may come up. These mechanics should be easy to add and shouldn’t cause significant changes in the core architecture. 

There are things that WILL require significant changes such as steam integrations like achievements and the steam workshop. 

I’m also exploring the idea of adding more than one goal to the game - to give a bit more depth as the levels progress. So maybe it's not just about getting your sandwich, but maybe you need to grab a drink and chips on your way to your sandwich?

I don’t know exactly, but I do know that I need to build the code for that functionality pretty early in the project as this could affect quite a few other systems.

I’d also like to explore some sort of scoring system to add some replayability to each level. This might be the classic 3-star system or something more numeric on a leaderboard? I don’t know, but again this likely needs to get built sooner rather than later. 

If you want to see the full “road map” for “Where’s My Lunch?” check out the notion page. There’s a list of everything that I’ve planned out so far… in varying detail. It’s a living document and will get updated as the project moves forward.

More Thoughts

But! The real beauty of this project is not how much can be added, but how few major systems need to be created. The game is functional. It just needs to be polished and integrated with Steam.

That’s not to say this is a short or easy project. There is a TON of work to do, but it’s the amount of work and the type of work that is manageable and doable. 

So what do you say? Do you have a small game gathering dust? Why not polish that game and release it? Who cares if you make money? Simply publishing a game to steam is a huge accomplishment in and of itself!!

So go blow the dust off your game AND stay tuned to the channel as this project gets started.