Saturday, November 21, 2015

Armoured Engines Dev Blog - Loading Screen

This post first appeared on the Bounder Games development blog at boundergames.com.

I recently had a fellow indie dev on Twitter asking how we made the Armoured Engines loading screen, so I wanted to share the process with all of you! This process can be used to make an animated loading screen using Unity 5.




The SceneManager


First of all, I have a group of "manager" game objects which all have the DontDestroyOnLoad() function called, so these objects persist throughout the game. One of these is the SceneManager, a singleton object that can be easily accessed from any script in the project. It does a few different things such as abstracting level, town, and map loading - but most importantly, it handles the scene transition.

Here is the function where all the magic happens:

public IEnumerator LoadScene(string sceneName, string music)
{
// Fade to black
yield return StartCoroutine(m_blackness.FadeInAsync());

// Load loading screen
yield return Application.LoadLevelAsync("LoadingScreen");

// !!! unload old screen (automatic)

// Fade to loading screen
yield return StartCoroutine(m_blackness.FadeOutAsync());

float endTime = Time.time + m_minDuration;

// Load level async
yield return Application.LoadLevelAdditiveAsync(sceneName);

if (Time.time < endTime)
yield return new WaitForSeconds(endTime - Time.time);

// Load appropriate zone's music based on zone data
MusicManager.PlayMusic(music);

// Fade to black
yield return StartCoroutine(m_blackness.FadeInAsync());

// !!! unload loading screen
LoadingSceneManager.UnloadLoadingScene();

// Fade to new screen
yield return StartCoroutine(m_blackness.FadeOutAsync());
}

As you can see, this function is a coroutine. I won't be going into the details of coroutines but they are basically awesome so I definitely suggest reading up on them. Without a coroutine this function would have to be handled using Update() and would be much more complicated!

Load the Loading Screen


We don't just want the loading screen to appear abruptly over our current screen - in game dev, you very seldom want anything to just appear. Instead, we will fade to black (since for our scene the loading screen is black), then load the loading screen, then fade the black away.

To do this we use three asynchronous methods. First we use a simple coroutine I wrote which is attached to a simple black sprite covering the scene: FadeInAsync(). This simple change the sprite's alpha from 0 to 1.0 over a set number of seconds.

// Fade to black
yield return StartCoroutine(m_blackness.FadeInAsync());

Once that coroutine returns, the screen is black and ready for our loading screen to be loaded. Here I use Application.LoadLevelAsync(), a built in Unity function. This unloads our current scene (aside from things marked DontDestroyOnLoad() such as our SceneManager and its black sprite) and loads our new scene.


// Load loading screen
yield return Application.LoadLevelAsync("LoadingScreen");

// !!! unload old screen (automatic)

Once LoadLevelAsync() returns, it's time to fade out our black using FadeOutAsync(), the reverse of our previous FadeInAsync().


// Fade to loading screen
yield return StartCoroutine(m_blackness.FadeOutAsync());


Loading the New Scene


Loading the next scene is a bit more complicated. I use Application.LoadLevelAdditiveAsync() to load in our new scene. This loads the new scene but does not destroy anything in our loading scene. This means that is going to have to happen manually! Don't forget this or you will end up with both your new scene and the loading scene active when the process is done.

float endTime = Time.time + m_minDuration;

// Load level async
yield return Application.LoadLevelAdditiveAsync(sceneName);

Another thing to note is that you will need to make sure your loading scene is on a higher layer than everything else in your new scene, or the new scene has any renderers turned off when loaded. Otherwise the new scene elements will draw on top of your loading scene.

Similarly, make sure any logic in your new scene is paused until your loading scene is completely gone - otherwise your character may die before the scene is loaded!

At this point, we chose to set a minimum amount of time for the loading scene to run, in order for it not to look jerky for very short load times. To do this, we simply wait for the remaining seconds that have not yet elapsed. This is completely optional, but if you use it make sure this time is quite short.

if (Time.time < endTime)
yield return new WaitForSeconds(endTime - Time.time);

This is also the time at which we chose to start our next scene's music, but that may be different for your project. We have a music manager which handles fading out old music and in new music using the PlayMusic() function.

// Load appropriate zone's music based on zone data
MusicManager.PlayMusic(music);


Unload the Loading Screen


Once the new scene is loaded in the background, it is time to get rid of our loading screen. Again, we don't want it to just instantly disappear, We face back in the black background first, again using FadeInAsync().


// Fade to black
yield return StartCoroutine(m_blackness.FadeInAsync());

Once the black background is faded in, we can get rid of the loading screen. However, there is no built in method to do this since the loading screen and new scene are now merged into the active scene. To get rid of the loading screen, we've created a separate singleton that lives on the root object of the loading screen called LoadingSceneManager. This singleton's sole responsibility is deleting it's object, though in the future we may add more functionality such as a loading bar or percentage display. For now we call a simple function UnloadLoadingScene() which simply destroys the loading scene's root object.

// !!! unload loading screen
LoadingSceneManager.UnloadLoadingScene();

At this point, if you have turned off drawing for your new scene, you should turn it back on before fading the black screen cover away.

With the loading screen destroyed we are free to fade away the black using FadeOutAsync(). At this point you may want to signal to your in game scene that the new level is ready to start, so game logic can be turned back on.

// Fade to new screen
yield return StartCoroutine(m_blackness.FadeOutAsync());


Potential Issues


When implementing this, we had several issues. First, the cameras in our title screen and in game level had different orthographic sizes, so when the new scene finished loading, the scene appeared to jump to a new size. For us this was simple as we hadn't actually intended for the cameras to be different sizes, so we simply fixed that error and things were fine, but if you do intend to have different sizes you should make sure you load your new camera during one of the black sections rather than during the loading screen itself.

We also had a problem with our UI from our new scene showing on top of our loading screen and black backgrounds. This is because our UI was set to use screen space overlay and could not have a rendering layer set. We solved this by tying the UI in each scene to it's camera, and settings a render layer below that of the loading screen. This may not work for everyone, so if you need your UI in screen space overlay you can may your black screen cover a UI object rather than a sprite and make sure it draws on top of your UI. You will also need to turn off the drawing of your UI until the black screen cover has faded in.

Hopefully this will help someone else make an animated loading screen! Feel free to ask any questions in the comments or contact me on Twitter @Jiyambi!

Monday, March 16, 2015

EGX Rezzed 2015

Bounder Games and I just got back from an awesome weekend at EGX Rezzed 2015. This was our first Rezzed and we thoroughly enjoyed it! I played lots of games, and watched others that I didn't get a chance to play first hand. I even got to chat with some of the devs, which was definitely my favorite part of the trip.

While there were tons of awesome games, these were my personal favorites:

Machiavillain by Wild Factor (@WildFactorGames)


A bizarre cross between Prison Architect, Dungeon Keeper, and a 50s B horror movie, Machiavillain oozes charm (or is that brain matter?). I had not heard of this title before stumbling across it on the show floor, but I absolutely fell in love with the cute and well executed art style and the sim/management gameplay. The dev was also a delight to chat to, and I'm really looking forward to seeing this game progress to its finished state.

Tembo the Badass Elephant by Game Freak


Like Machiavillain, I hadn't heard of this title before finding it on the show floor. I was initially attracted to the stand by the huge, comic book style poster featuring Tembo, the game's leading pachyderm. Though I came for the art, I stayed for the gameplay - it's no wonder Sega is publishing this fast paced platformer, as it bears a striking resemblance to early Sonic titles. I'm normally pretty tired of platformers, but this just felt too good! Full disclosure, I may have snagged some Sonic themed swag from the booth!

Big Pharma by Twice Circled (@TwiceCircled)


Big Pharma is a management game where the player attempts to dominate the pharmaceutical industry by hook or by crook (or by conveyor belt, which seems to be the preferred method). I've been watching this title for a while now. Unfortunately I wasn't able to play this at Rezzed, but I wanted to give it a mention anyway because (A) it looks awesome and (B) I got to chat to the developer for quite a while about Unity, other automation management type games, and other random stuff, and that was very cool! He seems like a rad guy and I can't wait to see where he takes this awesome concept.

Nom Nom Galaxy by Pixel Junk (@PixelJunkNews)


If I had to choose, this would probably be my game of the show. The gameplay is a little like Terraria in the perspective and diggable terrain aspects, but departs greatly from the crafting RPG in the direction of factory management with the addition of automated robots, corridors, air locks, etc. All with the goal of making the best soup this side of the Milky Way, and shipping it all over the galaxy. I've been looking forward to this title for a long time, and it's super hard to wait until it's out on PS4 so my friends and I can run around making soup together! The game is available now on Steam Early Access, but I think it suits console better due to the multi-player aspect. So colourful and stylish, with awesome automation based management gameplay that is exactly my cup of tea (or soup).

Monstrum by Team Junkfish (@TeamJunkfish)


Monstrum is a procedurally generated horror game - so it's a new pants-pooping experience every time you play! I didn't play Monstrum at Rezzed mostly because the devs are from my own little town of Dundee and I can play it whenever I like at home, but I can definitely recommend it as an awesome horror experience, especially if you happen to have an Occulus Rift to play it on.

Mutiny! by Hidden Armada (@HiddenArmada)


As with Monstrum, the Mutiny! devs are from good ole Dundee so I'd already played the game before Rezzed. Mutiny! is a multi-player cooperative/competitive game in which a group of pirates must defend their ship while simultaneously competing for the captain's hat. Also, there are pigs. The bedlam that ensues is as chaotic as it is enjoyable, and I'm really looking forward to bringing this out at parties.

Rezzed was an awesome experience, and it has left me inspired and energized to return to my own game dev projects. Thanks to all the developers, coordinators, journalists, and gamers that made the event happen - You rock!

Wednesday, February 11, 2015

Soulmates - Out Now!


So, I decided to release a game this week!

There's a game jam play party on Thursday, and we're invited to show off games from any game jam in the last year. My best game jam entry last year was a little puzzle game called Soulmates, made for the Ludum Dare. Naturally I decided to spruce up the game a small amount, get it fit for mobile, and show it off!

Then I realized - Saturday is Valentine's Day, and Soulmates is super cute and lovey-dovey. So, with only a few days before the play party, I decided to release the game! It's out now, free on the google play store. There's no ads or anything!

It was interesting throwing the game together for release in such a short amount of time. A surprising amount of assets are needed for releasing a game, even one as low key as this. We needed an app icon, banner image for the play store, descriptions, twitter promotion images, and more. We also decided to make a set of "business cards" for the game, similar to what we did for Combo Carts, to pass out at the play party. We managed to throw most of the assets together in a single night, update the music, fix the interface and account for multiple resolutions, and fix most of the bugs. It was rather exhausting!

We don't plan to make any money on Soulmates - we haven't even included ads in the game. If it does somehow become popular, we could easily sell puzzle packs for it. But that was definitely not our aim.

Releasing this game is not about money - it's about spreading some indie love. Too many games are about hate and violence. Soulmates tries to be as inclusive as possible, and celebrates an excess of cuteness and happiness seldom seen in games.

And hey, if our brand spreads a bit thanks to this, then awesome as well. But I don't really expect it to. This release is a gift from us to the gaming world, and we hope you like it!

Soulmates is available on the Google Play store: Get it here!