Saving Data to Create Longer Games

Exclusive offer: get 50% off this eBook here
OUYA Game Development by Example

OUYA Game Development by Example — Save 50%

An all-inclusive, fun guide to making professional 3D games for the OUYA console with this book and ebook

$23.99    $12.00
by Jack Donovan | May 2014 | Beginner's Guides Games

This article by Jack Donovan, the author of the book, OUYA Game Development by Example Beginner's Guide, covers implementing long-term skill progression and saving game data.

(For more resources related to this topic, see here.)

Creating collectibles to save

The Unity engine features an incredibly simple saving and loading system that can load your data between sessions in just a few lines of code. The downside of using Unity's built-in data management is that save data will be erased if the game is ever uninstalled from the OUYA console. Later, we'll talk about how to make your data persistent between installations, but for now, we'll set up some basic data-driven values in your marble prototype. However, before we load the saved data, we have to create something to save.

Time for action – creating a basic collectible

Some games use save data to track the total number of times the player has obtained a collectible item. Players may not feel like it's worth gathering collectibles if they disappear when the game session is closed, but making the game track their long-term progress can give players the motivation to explore a game world and discover everything it has to offer. We're going to add collectibles to the marble game prototype you created and save them so that the player can see how many collectibles they've totally gathered over every play session. Perform the following steps to create a collectible:

  1. Open your RollingMarble Unity project and double-click on the scene that has your level in it.

  2. Create a new cylinder from the Create menu in your Hierarchy menu. Move the cylinder so that it rests on the level's platform. It should appear as shown in the following screenshot:

  3. We don't want our collectible to look like a plain old cylinder, so manipulate it with the rotate and scale tools until it looks a little more like a coin. Obviously, you'll have a coin model in the final game that you can load, but we can customize and differentiate primitive objects for the purpose of our prototype.

  4. Our primitive is starting to look like a coin, but it's still a bland gray color. To make it look a little bit nicer, we'll use Unity to apply a material.

    A material tells the engine how an object should appear when it is rendered, including which textures and colors to use for each object. Right now, we'll only apply a basic color, but later on we'll see how it can store different kinds of textures and other data.

    Materials can be created and customized in a matter of minutes in Unity, and they're a great way to color simple objects or distinguish primitive shapes from one another.

  5. Create a new folder named Materials in your Project window and right-click on it to create a new material named CoinMaterial as shown in the following screenshot:

  6. Click on the material that you just created and its properties will appear in the Inspector window. Click on the color box next to the Main Color property and change it to a yellow color. The colored sphere in the Material window will change to reflect how the material will look in real time, as shown in the following screenshot:

    Our collectible coin now has a color, but as we can see from the preview of the sphere, it's still kind of dull. We want our coin to be shiny so that it catches the player's eye, so we'll change the Shader type, which dictates how light hits the object.

  7. The current Shader type on our coin material is Diffuse, which basically means it is a softer, nonreflective material. To make the coin shiny, change the Shader type to Specular. You'll see a reflective flare appear on the sphere preview; adjust the Shininess slider to see how different levels of specularity affect the material.

    You may have noticed that another color value was added when you changed the material's shader from Diffuse to Specular; this value affects only the shiny parts of the object. You can make the material shine brighter by changing it from gray to white, or give its shininess a tint by using a completely new color.

  8. Attach your material to the collectible object by clicking-and-dragging the material from the Project window and releasing it over the object in your scene view. The object will look like the one shown in the following screenshot:

    Our collectible coin object now has a unique shape and appearance, so it's a good idea to save it as a prefab.

  9. Create a Prefabs folder in your Project window if you haven't already, and use the folder's right-click menu to create a new blank prefab named Coin. Click-and-drag the coin object from the hierarchy to the prefab to complete the link.

We'll add code to the coin later, but we can change the prefab after we initially create it, so don't worry about saving an incomplete collectible. Verify whether the prefab link worked by clicking-and-dragging multiple instances of the prefab from the Project window onto the Scene view.

What just happened?

Until you start adding 3D models to your game, primitives are a great way to create placeholder objects, and materials are useful for making them look more complex and unique.

Materials add color to objects, but they also contain a shader that affects the way light hits the object. The two most basic shaders are Diffuse (dull) and Specular (shiny), but there are several other shaders in Unity that can help make your object appear exactly like you want it. You can even code your own shaders using the ShaderLab language, which you can learn on your own using the documentation at http://docs.unity3d.com/Documentation/Components/SL-Reference.html.

Next, we'll add some functionality to your coin to save the collection data.

Have a go hero – make your prototype stand out with materials

As materials are easy to set up with Unity's color picker and built-in shaders, you have a lot of options at your fingertips to quickly make your prototype stand out and look better than a basic grayscale mock-up. Take any of your existing projects and see how far you can push the aesthetic with different combinations of colors and materials.

Keep the following points in mind:

  • Some shaders, such as Specular, have multiple colors that you can assign. Play around with different combinations to create a unique appearance.

  • There are more shaders available to you than just the ones loaded into a new project; move your mouse over the Import Package option in Unity's Assets menu and import the Toon Shading package to add even more options to your shader collection.

  • Complex object prefabs made of more than one primitive can have a different material on each primitive. Add multiple materials to a single object to help your user differentiate between its various parts and give your scene more detail.

Try changing the materials used in your scene until you come up with something unique and clean, as shown in the following screenshot of our cannon prototype with custom materials:

OUYA Game Development by Example An all-inclusive, fun guide to making professional 3D games for the OUYA console with this book and ebook
Published: May 2014
eBook Price: $23.99
Book Price: $39.99
See more
Select your format and quantity:

Time for action – scripting the collectible

The first thing we'll script is some basic movement. Typically, the collectibles in games have some basic animation to differentiate them from the static objects in the scene, and it helps the player identify where they are. For our coin, just a constant rotation should suffice to make it stick out. Perform the following steps to script the collectible:

  1. Right-click on your Scripts folder in the Project window and create a new C# script named CoinRotation.cs.

  2. Double-click on the script to open it in your code editor. Add the following lines to the script's Update function:

    void Update() { gameObject.transform.Rotate(0, 0, 5); }

    These lines access whatever the GameObject script is attached to and call the transform property's Rotate function, which takes three parameters (one for each axis of rotation). We want to rotate our coin along the z axis, so we added a value of 5 in the Z axis parameter field and left the other values as 0.

    The transform property in every GameObject also includes the Translate function, which takes the x, y, and z arguments and moves an object along those axes rather than rotating around them.

    We don't need anything else from the rotation script; we'll be coding the collectible data on the coin as well, but it's a good idea to keep your scripts separated by purpose, so we'll leave this script as it is and create another one for a more advanced functionality.

  3. Save the CoinRotation.cs file and attach it to your Coin prefab by dragging it from the Scripts folder to the prefab. Press play to ensure that your coin now constantly rotates in the game.

    Next, we'll add another script that checks whether our marble touches the coin. When we created the primitive cylinder that our coin is based on, a Collider component was automatically added to it. Game objects use colliders to detect interaction with other objects in physical space, and they have several customizable properties in our coin's Inspector window under the Capsule Collider region, as shown in the following screenshot:

    The checkbox next to the Is Trigger prompt allows the object to collect the collision data without applying any automatic physical force, which is useful for item pickups and other nonphysical interactions. As we don't want our coin to impact our marble in any way when we collect it, we'll want to set the coin's collider to act as a trigger.

  4. Select your Coin prefab from the Project window, and locate the component labeled Capsule Collider in the Inspector window. Click on the box to the right of the Is Trigger prompt once to activate it.

    If you want to make changes to an object prefab, make sure that you apply the change to the prefab (in the Project window) rather than an instance of it in the scene (in the Hierarchy window). Changes made to individual instances of objects only change that instance, which is useful to differentiate instances, but this can lead to some puzzling inconsistencies if you don't keep the base prefab updated. To update a prefab, click on the Apply button in the Inspector window, or click-and-drag the updated object from the Hierarchy window over to the existing prefab to overwrite it.

    The other properties of the collider aren't important to us right now, but they're still relatively straightforward. The Physics Material property can accept any of Unity's built-in physical materials such as ice, which changes the way collisions and movement behave. The other position and orientation values all represent what part of the object the attached collider encompasses. When primitive objects create their own colliders, they're initially sized to fit perfectly around the shape, but they can always be adjusted to create a custom collidable area.

  5. Test your game in the Unity editor and try to roll your marble into the coin. You'll notice that even though there's an active collider on the coin, it doesn't physically impact the marble at all. This is because we have it set to be a trigger instead of a collision.

    Of course, simply not making an impact isn't enough of a functionality for the coin; we still need to make it interactive.

  6. Create a new script labeled CollectibleScript.cs in your Code folder, and open it in your code editor.

    To handle collision, we'll use one of Unity's built-in functions named OnTriggerEnter. This function is available on all objects with colliders, but it doesn't automatically add itself to scripts as the Start and Update functions do, so the first thing you'll need to do is add it to your new script.

  7. Add the following OnTriggerEnter function extension to your new script under the Update function:

    void Update() { } void OnTriggerEnter(Collider collidingObj) { }

    The single parameter that the OnTriggerEnter function takes is of the type Collider, which is a Unity-specific datatype that we can use to access all sorts of information about any collision that occurs, including the position, type, and game objects involved.

    We'll test this function using a simple print statement to output some text to the Unity editor. Add the following call to Unity's print function in your OnTriggerEnter function:

    void OnTriggerEnter(Collider collidingObj) { print(collidingObj.transform.position); }

  8. The only thing left to do is complete the link between your coin and your new script. Click-and-drag CollectibleScript.cs and release it over your Coin prefab to add it to all the instances of that prefab.

  9. Click on the play button in the Unity editor, and roll your marble into the coin as shown in the following screenshot:

    You'll notice that as soon as the collision occurs, a print statement will appear in the bottom-left corner of the Unity editor, which outputs the location of the collision.

    Every time you roll away from the coin and then roll back into it, it will reprint the collision data because the OnTriggerEnter function is called every time a collision is entered between two objects with colliders.

    In addition to the OnTriggerEnter function, the Unity API also contains an OnTriggerExit function that gets called when two objects separate from a collision, and an OnTriggerStay collision that gets called for every frame that a collision occurs. Each of these are useful in their own way when creating your game.

    In the case of our coin, we don't want our player to be able to pick it up more than once, so we'll tell the coin to delete itself as soon as a player collects it.

  10. Add the following lines to the OnTriggerEnter function in your CollectibleScript.cs file:

    void OnTriggerEnter(Collider collidingObj) { print(collidingObj.transform.position); Destroy(this.gameObject); }

If you test the scene in the Unity editor again, the coin will disappear as soon as the marble touches it. Next, the coin will need to notify the game somehow that it's been collected, and then we can save that data so that the total number of collectibles is persistent.

What just happened?

Now is a good time to look back at what you've done so far. With the two scripts that you wrote, you've made your coin a fully functioning object in the game world.

Even though the code contained between the two scripts could have been put into one, it's a good practice as a developer to keep your scripts separated by purpose, so if you ever want to change the specific functionality of one aspect of an object, you can change that aspect's code without having to look through or change anything else.

You also utilized Unity's built-in OnTriggerEnter function for the first time. This function is extremely useful for handling interactions between objects in the game world, and the collider parameter provides a wealth of information about both objects involved in the collision, which you can use to collect data about your game.

There's a function similar to OnTriggerEnter that works with non-trigger colliding objects; this is useful for colliding objects that make an impact, such as bullets or balls. This function is named OnCollisionEnter, and the only thing that differentiates it from OnTriggerEnter is that it takes a Collision type as the parameter and not a Collider type. The collision data carries a few more values with it, such as impact force and momentum.

The only thing left to complete the collection event is tell the player that they've collected something, and we'll do this by accessing the player's script from the collider data.

Time for action – accessing the scripts on other objects

The only existing script on our marble right now is the input script, so create another one that will be responsible for handling the collection data. Perform the following steps to access the scripts:

  1. Create a new file named PlayerCollection.cs in your Scripts folder and open it in your code editor.

  2. Add the following variable and function to your code, above and below the Start and Update functions, respectively:

    private int totalCoins = 0; // Use this for initialization void Start() { } // Update is called once per frame void Update() { } public void CollectCoin() { totalCoins++; print(totalCoins); }

    Let's look at the two things you just added to your script. We set the int variable to private because this script is the only one that needs to edit that value, but we added a public function that increments the value by one.

    This method is better than directly accessing a public variable on another script because making variables private ensures that objects interact with the data only in ways you've defined.

    Next, we'll call the CollectCoin function from the coin as soon as it's collected. We'll do this by accessing the player object with the collider variable in the OnTriggerEnter function.

  3. Open your CollectibleScript.cs file, and add the following line to the OnTriggerEnter function:

    void OnTriggerEnter(Collider collidingObj) { print(collidingObj.transform.position); collidingObj.GetComponent<PlayerCollection>().CollectCoin(); Destroy(this.gameObject); }

  4. Test your new code in the editor and ensure that the print statement you wrote to display the total number of coins appears when the coin is destroyed.

  5. Try adding several coins to the map and rolling into all of them to see the print statement's value get higher with each coin you collect.

Now the marble and coins should appear as shown in the following screenshot:

What just happened?

A lot of games require an immense amount of interaction between objects, so cohesion between code systems is a must. Unity's GetComponent function is a great way to access values from other scripts on any object in your scene.

Using the data returned from a trigger collision using Unity's built-in collision detection, we got the PlayerCollection data from the colliding marble game object and were able to call a public function that incremented a private variable representative of the total number of coins collected by it.

Later, you'll learn other ways to access other game objects from within a script; but for now, we'll focus on saving the data that you've already collected so that it reloads whenever the game is started.

At this point, the number resets whenever you stop the game and press play again, so next we'll save the data every time a new coin is collected.

Saving data with the Unity engine

Now that our prototype features a collectible that we can save, we need to actually program the saving operation using Unity's built-in data storage methods. In this section, you'll save data and see it loaded even after you close the game and reopen it.

OUYA Game Development by Example An all-inclusive, fun guide to making professional 3D games for the OUYA console with this book and ebook
Published: May 2014
eBook Price: $23.99
Book Price: $39.99
See more
Select your format and quantity:

Time for action – saving data with PlayerPrefs

We're finally ready to save and load data to the player's collection class. For this, we'll be using Unity's PlayerPrefs class in code.

  1. Open the PlayerCollection.cs file in your code editor and add the following lines to your CollectCoin function:

    public void CollectCoin() { totalCoins++; print(totalCoins); PlayerPrefs.SetInt("TotalCoins", totalCoins); }

    It's as simple as that! Let's examine what this new line actually does. We're calling the SetInt function from the PlayerPrefs class because the data we're saving is an integer value. However, PlayerPrefs also contains functions to save floating-point values and strings.

    No matter what kind of data you're saving, the parameters for the saving function are generally the same. The first value is a text string that represents a key or a way to label the data you're saving so that you can access it later.

    The second parameter is the data you're saving with that key, be it an integer, string, or float. Of course, sometimes you may want to save data that doesn't fit any of those three datatypes; for example, the C# Color datatype. In such instances, you would need to program a way to translate the value into one of the accepted datatypes. One way to do this would be to program a function that saves and loads integers for the color value (red = 1, orange = 2, and so on).

    Next, we'll set up our PlayerCollection class so that it loads in our saved value as soon as the game starts.

  2. Add a line to the Start function in CollectibleScript.cs as follows:

    // Use this for initialization void Start() { totalCoins = PlayerPrefs.GetInt("TotalCoins"); }

    As the Start function of PlayerCollection gets called once at the beginning of our game, we can use it to assign our last saved value to the totalCoins variable to ensure that we pick up right where we left off.

  3. Add a print statement to the Start function, which will print out the value of the totalCoins variable right after it's loaded:

    // Use this for initialization void Start() { totalCoins = PlayerPrefs.GetInt("TotalCoins"); print("Total coins on load: " + totalCoins); }

  4. Click on play, collect some coins, stop the game, and then click on play again. You'll see your number of previously collected coins in Unity's editor output, confirming that your save and load operations are now fully functional.

What just happened?

At this point, the data that you save is completely up to you. You can use integers, strings, and float datatypes in tandem with the saving and loading functions to record virtually any value that needs to be carried through to every play session.

Just being able to use Unity's saving and loading expands the scope of any of your games greatly because you can now set goals that take longer than a standard play session to complete and not worry about your player losing their progress.

The only thing your prototype is lacking now is a visual representation of the data that you've saved. To show the total number of coins collected without using Unity's output window, we'll use something called GUIText.

Summary

In this article, we saw how to create collectibles, their scripting, and accessing the scripts on other objects. We also saw how to save data so that whenever we start a new session, we can have previously saved data available.

Resources for Article:


Further resources on this subject:


About the Author :


Jack Donovan

Jack Donovan is a game developer and a co-founder of Team Aurora Games, an independent game studio located in Burlington, Vermont. He founded Team Aurora Games with a group of his college peers because they wanted an outlet for creative projects that could eventually evolve into fully marketable games. He has been coding games in the Unity game engine since 2010, and has been working with the OUYA console ever since the initial developer kit release in 2012.

He programs primarily in C#, C++, Objective-C, and JavaScript. He has extensive experience in the DirectX, XNA, and Unity libraries and has developed his own homemade engines as well.

He is also a passionate technical writer. He has contributed DIY/instructional articles to Wired.com and Popular Science magazine, covering several unique hardware and software projects.

He studied at Champlain College, which he graduated from in May 2014 with a Bachelor of Science degree in Game Programming.

When he's not making games, he loves playing them with friends and discovering new ideas and concepts. He's an avid music listener and coffee drinker, both of which helped make this book possible.

He can be reached at jack@teamauroragames.com, and all of his present and future projects can be found at teamauroragames.com.

Books From Packt


Ouya Unity Game Development
Ouya Unity Game Development

Unity 3.x Game Development by Example Beginner's Guide
Unity 3.x Game Development by Example Beginner's Guide

Unity 3.x Game Development Essentials
Unity 3.x Game Development Essentials

Unity 3D Game Development by Example Beginner's Guide
Unity 3D Game Development by Example Beginner's Guide

Unity 4.x Game AI Programming
Unity 4.x Game AI Programming

Flash Game Development by Example
Flash Game Development by Example

Learning Libgdx Game Development
Learning Libgdx Game Development

OUYA Game Development Essentials
OUYA Game Development Essentials


Code Download and Errata
Packt Anytime, Anywhere
Register Books
Print Upgrades
eBook Downloads
Video Support
Contact Us
Awards Voting Nominations Previous Winners
Judges Open Source CMS Hall Of Fame CMS Most Promising Open Source Project Open Source E-Commerce Applications Open Source JavaScript Library Open Source Graphics Software
Resources
Open Source CMS Hall Of Fame CMS Most Promising Open Source Project Open Source E-Commerce Applications Open Source JavaScript Library Open Source Graphics Software