Hire Us For Your Game!

Using Moonsharp in Unity Projects : Application for Customization

System Re-Use versus Customization

The New Super Mario Bros series was already being seen as a bit “safe” and “predictable” upon its second entry, New Super Mario Bros Wii. The series, a spiritual and design continuation of original Super Mario Bros formula from the 80s didn’t exactly flip the table with each new entry.

  • Plot : Rescue Princess Peach from Bowser
  • Gameplay : Mario-Style momentum-based platforming.
  • ???
  • Multiplayer : Mario x 4

That last one was the big new innovation for this second entry, which doesn’t mean much for single players. You’ve got a friendly grass world to begin, a fiery castle world to end, and desert, snow, etc worlds in between. The game’s intentions were clear– use a safe and popular format to fill a release schedule and make some consistent cash– not a bad plan, but not one that is going to stick in players minds or (re)establish a brand or some characters.

And yet I do have one distinct memory from this game.

only the second in the series, was already being seen as bit “predictable” upon launch.

The finale of UnderTale includes a sizeable exposition dump– line after line of various monsters revealing a critical piece of the game’s backstory. With monster-squad after monster-squad saying line after line in sync, it’s a moment that’s likely to make some players daydream.

Watch the sequence here.

But this isn’t what happens. At the most important and emotional moment of this otherwise samey sequence, one of the monster squads chokes up and breaks syncronization with its partners, in a way that catches the player’s attention again.

![An NPC breaks tendency to get the player’s attention back before announcing the death of an important character.](https://d2vansag56dj8u.cloudfront.net/ShareX/2020/December/26/19/20/29/007/5d490952-fa29-448b-b28c-642434797fb4/2020-12-26_19-20-04.webp

This timing isn’t a coincidence. Breaking tendency– establishing a standard and then deviating from it– is a way to generate surprise and acquire the player’s attention.

At the heart of many excellent games is the struggle between quantity (standardization) and quality (expressiveness). New Super Mario bros was a cheap game to make– a safe bet– and its most memorable moments are when it decides to go off-script.

Standardization cuts costs massively, which is why a game with several systems will re-use them for hundreds of battles, why mini games are often rare,

MoonSharp as a means of easy Customization

When a system is established, such as an environmental system.

Unfortunately, in its current state the Moonsharp open source project has nasty garbage-generation problems by default, with every execution of a script generating quite a bit of garbage and slowdown if too many executions occur. This fact makes MoonSharp useful for periodic, occasional calls rather than every-frame logic.

Moonsharp finds its use via configuration interrupts (maybe not surprising, as the language was initially designed for lightweight config usage).

Establishing an Interrupt

Below is part of our “Gameplay Initialization” sequence. When a map is loaded, this portion of logic is responsible for establishing what the environmental affects of this stage will be.

The DetermineNextStage function is responsible for figuring out what the next stage is. It’s written in C#, and provides a default random-selection approach that should work well enough for most games.

C# random stage selection code

What if a game wants to do something specific and customized, though? For example, a game might decide that “round 1” should always take place in a certain location, with a certain cinematic camera position, etc. How can we make this possible?

Let’s add a Lua Interrupt–

Adding a Moonsharp Lua Interrupt

This new function locates the relevant LUA code for this interrupt, and provides the LoadGameplayConfig data structure to this LUA code for customization purposes.

The LUA code has a function “GetRoundConfig” that takes two parameters– a round number and the gameplay config data structure mentioned above. Based on the round, it will alter this config data structure to turn certain features on and off–

For instance, in the logic above you can see that this particular game (represented by a spreadsheet) wants the first round to always occur in stage “s1” (note that “Play Now” is our version of survival / ladder / arcade mode), with no posse (allied units) spawned in, no UI, no intro camera animation, a particular camera zoom level and position, etc. If you look at the “round 2” block of logic, the situation is similar, however, the abilities UI is no longer disabled.

These things are done for guidance purposes– as a game begins, it can be helpful for a game design to gradually introduce new mechanics, new UI, etc. However, another game may not care about this, or desire a different intro pipeline. These games (represented by a different spreadsheet fed into the engine) may choose to supply a different LUA interrupt or even none at all (to roll with the purely-random stage selection approach).

As new needs for expressiveness arrive in our games–

  • May we begin a YarnSpinner dialogue sequence at the beginning of the stage?
  • May we interrupt enemy-spawning in a particular stage to give all enemies hats in accordance with the plot?
  • May we block the player from using any abilities in this stage?

New interrupts begin to appear throughout the codebase, allowing configuration data structures to be customized before use as a function of more and more variables (round number, player dialogue decisions, etc).

Subscribe to Future Devblogs!

Subscribe here to be notified when we publish future devblogs, tutorials, etc.