Scripting Lessons I Learned at My First Job in Game Development
In this post, I share most important things I learned scripting systems and content in a mobile farming game with strong narrative.
![](https://eu-images.contentstack.com/v3/assets/blt740a130ae3c5d529/bltba62518415cda0e2/652fe6ddbc479f8697ef691f/default-cubic.png?width=1280&auto=webp&quality=95&format=jpg&disable=upscale)
For the last 8 months, I’ve been working on Summer Tales as a scripter and game designer. I participated in the development of the game as well as in post-release support stage. And since Summer Tales is a free-to-play mobile game, the initial release was just the beginning.
I learned a lot during both of these stages but mostly after the release when I saw consequences of my good and bad decisions.
In this post, I focus on scripting part of my work. To be clear, when I use ‘scripting‘, I mean high-level programming-like tool for implementing gameplay logic in the game. It is usually unique to a project or studio and easy-to-learn. Here’s an abstract example of a script that executes a cutscene when requirements are met:
requirements = [ run_once(), level(10), location("town") ] actions = [ focus("mayor"), say("mayor", "Hi there!"), fade_in(), remove("mayor"), fade_out() ]
As I said, I learned a lot. I put these lessons in a form of advice that I’m going to give my 8-months-ago self when I finally invent a time machine. Here we go.
Keep things modular
At first, making parts of the game easy to enable/disable or recombine without breaking the whole game or rewriting a lot of code may not seem worth the trouble. Especially when everything works well as it is, and considering that a modular system requires time and effort to build.
But things change all the time, especially in f2p projects, and modularity will pay off. For example, in Summer Tales, the narrative consists of separate storylines. For the sake of optimisation, each storyline has overarching start requirements (such as a certain level of the player). If these requirements aren’t met, the game won’t scan requirements of all the cutscenes inside of the storyline, so they will never start.
But what if we wanted to move a storyline to a later moment in the game. Say, currently players get access to it after they reach level 10, and we want to make it available only after level 15. So we simply change overarching requirements of the storyline, and it’s done! Although… what about players who are in the middle of this storyline when they download the update? They won’t be able to finish it until they reach level 15 (because everything inside the storyline will be blocked until level 15), and, considering how our stories are written and designed, we don’t want that. What’s more, other storylines don’t break into parts like that, so it would create inconsistency for the player.
The solution is that we have a ‘gatekeeper’ script that checks a player’s level. If they have a sufficient level, the gatekeeper enables the storyline. The storyline itself doesn’t have any level-related requirements. Even if we move it, players will be able to finish the story without a break, since, once they are past the gatekeeper, they won’t lose access to the storyline. If we use this approach, our storylines become building blocks that we can safely move as we want, managing it with gatekeeper scripts. In Summer Tales, such gatekeepers were small and simple so that even non-scripting designers could configure them on their own.
A/B tests are trickier than they seem
If you’re making an f2p game, then most likely you do a lot of A/B testing. It’s important to allow for future tests during the initial development stage. It’s useful to keep a list of planned tests at hand. When designing scripts, look at it and ask yourself questions like, ‘How am I going to implements this test?’, ‘How should I design my system so that I don’t have to rewrite it to launch a new test?’
Another thing about A/B tests is how to restrict content to a test group. Say, we have a new mechanic — ships that exchange farm produce for overseas goods. We want to test this mechanic. An obvious solution is to create a requirement by a user’s test group. It will look like this:
requirements = [ ab_test("ships") ]
So, we have ‘ships’ and ‘no_ships’ test groups. Everything works just fine. For now.
Later test results show that ships mechanic performs great. Therefore, it becomes a constant part of the game. When we launch a new test, say, ‘collections’ and ‘no_collections’, the requirement above that looked clean and simple, now looks different:
requirements = [ any([ ab_test("ships"), ab_test("collections"), ab_test("no_collections") ])]
When we restrict content to a test group, later we have to include all group that have this content. With time and new tests, it gets even worse.
The solution is to manage accessibility by a feature type rather than a test group. In this case, each test group has a list of features it includes (in Summer Tales, it was an array of strings in a test group configuration file), and the script looks in that list to determine whether to give access to some content or not. The code looks like this:
requirements = [ feature("ships") ]
Using this method, whenever we launch a new test, the script stays the same. This is especially important if you have numerous pieces of feature-restricted content, which makes it easy to forget about one and screw up at some point.
When you test a relatively small feature or alteration (say, a colour of a button), it’s usually easy to merge a test group with the basic group later. But big features or a different combination of the narrative may be tricky to merge. However, if you think about it in advance and allow for future merge in a script, you’ll save yourself the trouble.
Ensure migration in advance
This is somewhat connected to the previous point. As your game grows in complexity and size, it becomes harder to implement new features or make changes to existing content, since it mustn’t break game for current users. Migration was one of the most difficult things I had to deal with. Whenever you script a system or content in your game, regardless whether it’s subject to A/B testing or not, consider how you’re going to implement changes in it in the future, and how these changes can affect existing players. What will happen to users who haven’t encountered this content or system yet? And to those who are past it already? And, most importantly, what about players who are in the middle of something you’re about to change? Allowing for it in advance will save you a lot of time and effort, which you can invest in developing new features.
***
In a perfect world, there’s always time for well-thought design and implementation. In real life, however, we have to compromise. When you encounter this situation, carefully choose which parts of your system are vulnerable points (most likely to change or need extensive support or may cause trouble in the future). Focus on them. Well-designed, modular and prepared for future changes system pays off in the long term.
About the Author
You May Also Like