Unity - Undefined Script Order of Execution Bugs
When a bug consistently reproduces for one person but never does for another, or it appears in a build but not in editor or vice-versa, it may be from an undefined script order of execution bug. These bugs occur regularly if using Unity normally, and the cause is subtle. The inconsistency also makes them a pain to track down and fix. These are easily the worst type of bug out there due to their inconsistent nature. This document will explain how these bugs happen, demonstrate the reasons for inconsistency in whether they occur or not, methods for fixing them, and ways of designing your code so there’s no room for these bugs to manifest in the first place. To start, here's a simplified example of a script execution order bug, shown below:
class CharacterUI : MonoBehaviour
For someone testing the game, they may always, consistently, get a null reference exception in Start() of CharacterUI, resulting in a broken-looking UI. For the developer and several other testers, the bug may absolutely never happen. The bug may also never happen for anyone in-editor, but does happen for some people in builds.
Why does this happen?
First, let’s establish that the CharacterUI’s Start() depends on Skills running Start() before it. Otherwise, when it would access the data within characterSkills.skillsKnown, skillsKnown would be null. With that in mind, for the above case, what actually defines the order the two Start() methods run? The order of execution for Start() between these two classes is completely undefined. Because it’s undefined, if these two objects are created at the same time at scene startup, Unity determines this order arbitrarily, and it varies between in-editor sessions and builds, and per machine! For some people the bug may always happen in editor and never in build, and others it may always in the build and never in editor, and for others it may never happen at all. This all depends on whether, for a given machine and build/editor session, Unity happens to decide if Skills runs before CharacterUI, or CharacterUI runs before Skills. While we work through the example, consider that for an actual codebase in a game, the classes involved in such a bug will be more numerous and complex.
There's a few solutions available for our contrived example. One solution would be changing Skills to initialize on Awake(), which will always run before anything else’s Start(). But what if for your case’s current logic, both need to use Awake() or both must use Start() due to other dependencies from other classes? If both use Awake, you'd run into the same issue, as the order between the two Awake calls are undefined. If both must use Start, it’s the same as the example undefined order problem.
The general solution requires explicitly defining the script's priority/order. There's a way to do this in the project's script settings, but it's a pain to manage it there (and gets out of hand as you get into hundreds of classes), so you can instead use an attribute on the class, which looks something like [DefaultExecutionOrder(150)]. Below I show the attribute applied to the classes to fix the bug.
The lower the order value, the earlier its mono methods like Start() are executed relative to other monobehaviours. Now, the execution order for the Start() calls has been defined, so Skills Start() will always run before CharacterUI's Start(). Note, this execution order affects Start, Awake, Update (and all other types of update like FixedUpdate, LateUpdate), as well as OnEnable, OnDisable. For example, the Skills’ class OnEnable() would run before CharacterUI’s OnEnable().
Note: if just one class had its order defined, such as CharacterUI’s, the bug could still occur as Skills’ order relative to it is still undefined.
Preferred Solution - Avoiding this Problem By Design
The above solution of using the DefaultExecutionOrder attribute is fine if the damage is already done and the code can’t be refactored. However, the ideal solution is to design your code in a way where this issue doesn’t have room to occur in the first place.
Solutions for this problem at a design level involves avoiding using Start() or Awake() for anything which depends on another game object's state. Instead, you should have some dedicated code in another class responsible for initializing your objects and using them together, rather than having your individual objects cross-referencing each other. As a red flag, if you require your Start() or Awake() methods to run in very particular orders between separate objects of classes in order for them to function properly, they should be redesigned so they are initialized explicitly by hand in another class. The thought process behind this is that if their initialization order is so important for them to function at all, this order deserves to be explicitly defined by hand, line-by-line, in one location, and not spread out throughout the codebase by using the DefaultExecutionOrder attribute. Let’s look into an example.
For a contrived example, imagine you have classes A, B, C, D, and E which all depend on each other in different ways in their Start() and Awake() methods. If you need to understand the order which they initialize and you’re using the DefaultExecutionOrder attribute, you’d need to go between each class and make note of their order number, then organize those order numbers lowest to highest, then separately consider how for this order their Awake()s run first, followed by their Start()s, and some classes may be missing one and have the other. There is a much clearer way - just introducing one simple class, which takes references to each involved class and explicitly initializes them in a manually defined order, passing their dependencies as arguments.
Now, the order of initialization to a developer is extremely clear by just looking at it, the dependencies between classes are also extremely clear, and very importantly, there’s also no room for undefined execution order bugs because you have explicitly defined the initialization order by hand.
Note that when defining initialization orders by hand, you may encounter things called cyclic dependencies. For example, if system A requires B to be initialized, and B requires C to be initialized, and C requires A to be initialized, there’s no possible valid order to initialize them in. Resolving cyclic dependencies can be complicated and requires some sort of refactoring, so it’s outside of the scope of this document. Resolving cyclic dependencies is the expression you’d want to use when researching solutions.
Little Medusa's Multiplayer Roster
Hello there to all the stone pushers! We know you’ve been waiting for an update on Little Medusa’s multiplayer mode, and today, we’re finally allowed to spill some details on how our take on the battle royale formula is shaping up!
So how would the game play out? We’re happy to report that we feel like we’ve successfully translated Little Medusa’s core gameplay mechanics and transformed it into a multiplayer experience that would be easy to learn for all kinds of players (while still providing enough wiggle room to show off a skilled player’s mastery over the game’s unique mechanics). Each player will still be trying to utilize the pushing mechanics of the single-player mode to eliminate the other players and be the one to come out on top.
There are also quite a number of settings that you can adjust to fully customize your playing experience. You can toy with the number of lives that each player starts with, the amount of time that you have to duke it out, one-hit kills, item spawns, and even enemy spawns. No matter how you adjust your options, the end goal remains the same: be the last one standing!
As for the specifics, we’ll slowly be rolling out details for the twelve maps and the myriad of items and enemies. For now, let’s give you some specifics on the five playable characters, with each character having two different attacks! Of course, we can’t have a character roster for Little Medusa without the titular character herself: Medusa plays pretty much the same way she does in the main game. She can petrify enemies with her Petrification Gaze. Petrified enemies can be pushed into walls for damage or pushed off of the level. Her other attack, Boulder, creates a boulder that blocks movement and also acts as a wall. These two attacks can be combined in clever ways, allowing Medusa players to create a boulder to smash petrified enemies.
Our next two characters are Poseidonna and Heliemis. Poseidonna can create waves that push everything in their path off of the level with her Tidal Wave attack. She can also create a small bubble in one adjacent space to push enemies with her Bubble Shield. Both of her attacks don’t do any damage, but her insane pushing power makes her a force to be reckoned with. On the other hand, Heliemis’ Mighty Wind attack creates a gust that travels up to three spaces away and deals damage. The Tornado Pull attack creates a tornado that lasts for five seconds, pulling enemies in if they go near it. This allows for some wicked setups to trap an enemy in a tornado while blasting them with Mighty Wind.
Our last two characters, Ermolai and Averna, round up the elemental quartet. Ermolai is the earth dude, and he’s all about creating holes in the ground. Pitfall doesn’t do damage but instead creates a hole in the ground. While this may seem underwhelming at first, if you manage to put a hole underneath your opponents, they instantly lose a life! His other ability, Earthquake, creates four holes around a 2x2 area, but it does resolve slower, so it’s trickier to make enemies fall off of it. It’s a good thing that it does damage! We’re only missing the fiery member of our group, and that’s Averna. She’s probably the simplest character in the game, but also the most damage-heavy. Both of her abilities will deal damage, with Fireball being a simple projectile that will deal damage upon contact and will deal additional AOE damage around it. Flame Pillar also does damage, but travels slower and has a lower overall distance, but utilizing both of her abilities can allow Averna players to take control of the battlefield.
For now, we’re still at the balancing stages of the game, so we’re still checking out the numbers on these characters, but for the most part, these attacks are pretty much ready to roll. We want all five of them to be playable in some amount instead of any single one of them to dominate the playing field. We’ll definitely be telling you more about the other aspects of the game in the future, though, so stay tuned to our social media channels for further updates! You can also head on over to Mega Cat Studios for more retro gaming goodness.
Product Page Creative Requirements
Setting up a product page for your apps comes with a lot of surprisingly specific details that you have to follow. This is especially egregious with product pages for mobile platforms due to how the Apple App Store and Google Play Store handles them differently. But don’t fret, because we’ve handily prepared this sweet cheat sheet to help you with figuring out how to best tackle the mobile market’s finicky product pages.
Each element of your product page has different requirements depending on the specific platform they are in, so read on to know exactly what to do and what to avoid.
APPLE APP STORE
The App Store puts screenshots as one of the first things that you see after accessing the product page, they make up a good chunk of a user’s first impression of your product. So always make sure that your screenshots are optimized and your other elements after the scroll follow-up on that impression.
Content-wise, the rules for screenshots are easy to follow: They actually have to be screenshots. Images like real people having fun while looking over a phone’s screen are not allowed. However, the sizes are a bit more specific because you need to have screenshots for multiple types of devices.
A screenshot taken from a minimum 5.5 inch display for iPhones should be included, as well as ones that were taken from at least a 12.9 inch display for iPads. You can have a minimum of one and a maximum of ten for each type, and they can be either in portrait or landscape. Remember that these screenshots are what makes the majority of a user’s first impressions, so displaying screenshots that are both impactful and meaningful will greatly affect how a user perceives your product.
If you’re unsure if your screenshot would fit within Apple’s guidelines, you can check out their official list of screenshot specifications here.
Much like screenshots, preview videos are also one of the first things that a user sees in the App Store after accessing your product page. These videos are actually located on top of the screenshots, so they are more of an immediate draw than the screenshots. However, these videos will automatically play on mute by default, so you always have to make sure that your videos will draw users in even without the sound turned on.
Like screenshots, preview videos can either be in landscape or portrait, and a complete list of specifications for them can be found here.
When users simply browse the market instead of searching for your app directly on the search bar, an app’s icon will be the first thing that they will be seeing. This is why app icons need to stand out from a sea of similar looking icons. It has to encapsulate your product’s feel in one neat looking icon. Even when the user is scrolling through the app page, these icons will remain atop the page in a sticky header, putting even more pressure on it to deliver your product’s overall quality.
Unlike the screenshots and preview videos, the app icon is pretty simple when it comes to specifications. They either have to be 512 x 512 pixels or 1024 x 1024 pixels, and they can be either in .jpg or .png formats.
There are two types of page artwork: product page artwork and developer page artwork. Product page artwork will be needed in case your product is chosen by Apple to have its own artwork on the product page. It’s also needed for some of your product’s crowning moments of being featured in the App Store’s Today tab, which is a tab that highlights specific products in various blog style posts. Developer page artwork, on the other hand, is used to spice up your developer page, possibly leading to the user looking at the other products you have in store for them.
However, no matter which type of page artwork you need, they will usually be cropped depending on what device your users are currently using. So it’s a good practice to have your page artwork’s most important elements be grouped up together in the image in such a manner that they can be framed nicely no matter which device that the page artwork is currently being viewed on. For more specifics on formatting your page artwork, you can view the official guidelines here.
GOOGLE PLAY STORE
Like the App Store, screenshots in the Play Store are also seen immediately by the user even before they scroll down the product page, so it’s a good idea to have similarly impactful screenshots. You can have a minimum of two screenshots and a maximum of eight in either landscape or portrait formats.
However, unlike the App Store, screenshots for the Play Store are looser in terms of actually being screenshots. You can have promotional artwork for in-product events, a composite of screenshots and elements from your product, and so on, as long as they follow specific requirements. Screenshots for the Play Store should have a minimum dimension of 320 px and a maximum of 3840 px, and the aspect ratio can’t be higher than 2:1 or 1:2.
Unlike the App Store, videos on the Play Store are optional. However, if you decide to add one on your product page, you’ll find that they are actually YouTube videos that will only play once the user decides to access them. They will be displayed in the Play Store as video thumbnails with a play button overlaid on them.
These videos should have a minimum length of 30 seconds and a maximum of 2 minutes. Since they are formatted as YouTube URLs, the video’s size is actually a bit flexible, but 1920 x 1080 would be a great size to use.
This is where there will be the least number of differences between the two stores. App icons will always be front and center when creating first impressions, so no matter the platform, your app icon should always pack a punch.
However, size specifics are a bit different for the Play Store. They can only be 512 x 512 pixels large with a maximum of 1024 kb in terms of file size, and only png formats with alpha will be accepted.
Creating a strong impression for these seemingly tiny details can make all the difference in the world for your product. If any single one of these elements can catch a user’s curiosity, they can turn a browse into an install. And the more iconic any of these elements are, the higher the chances of such an event happening, which, in turn, will mean greater success for your product. Who knows, maybe this would turn any one of your blueprints into actual future endeavors.