How To Save Player Data Locally And Remotely For Cross-Platform Games

The Need for Persistent Player Progression

Saving player data is a critical component of modern video games to retain players and enable progression across devices. As games have evolved from simple arcade-style games to complex multiplayer environments, the ability for players to save their progress and pick up where they left off has become extremely important.

Without saved games, players would have to start over every time they play. This leads to frustration when progress, stats, or any achievements are reset. Even casual mobile games now utilize some form of cloud or local saving to keep players engaged by giving a sense of ownership and persistence.

Persistent player data also allows cross-device play. A player can switch from their phone to tablet to PC and continue with the same world, character, and items they have accrued. This unified experience leads to much higher customer satisfaction and engagement.

Common Approaches for Saving Game Data

PlayerPrefs for Local Data Storage

One of the most basic methods of saving simple game data is through a platform’s player preferences system. For example, Unity provides PlayerPrefs which stores key-value pairs locally. This works well for small pieces of data like options or basic stats.

PlayerPrefs data persists between game sessions. It is limited to basic data types and small memory sizes, but provides an easy introduction to locally saving data for things like high scores, sound options, or basic progression.

Remote Databases for Cloud Saving

For online and mobile games, remote databases provide secure cloud storage for game data. This allows progression across multiple devices through account-based data storage on company servers.

Popular backend-as-a-service providers like PlayFab, Firebase, and Amazon Web Services make cloud storage easy to implement. Complex data can be serialized into formats like JSON or XML to upload to the remote database.

Remote saving is more complicated but enables advanced features like shared worlds, account-based profiles, item trading economies, and massive multiplayer experiences.

Serializing Game Data to JSON or XML

When saving more complex game data either locally or to a remote database, object serialization is used. Popular serialization formats include JSON and XML which convert game object data to text.

For local saving, this compressed string of data can be written to a file. For remote saving it is uploaded to cloud databases. Custom classes and complex object hierarchies can be serialized this way.

Serialization frameworks like Newtonsoft Json.NET for Unity provide methods to serialize and deserialize data automatically for classes you define. This simplifies the process greatly compared to manually converting data.

Choosing the Right Solution

There are a few key considerations when choosing a game saving solution:

  • Target platforms and capabilities
  • Data privacy needs
  • Infrastructure costs

For example, a single-player PC game can utilize local file storage easily while a cross-platform online game will lean towards remote databases for unified saving. Data sensitivity also varies between types of games.

Weighing factors like these help choose the most practical saving approach before implementation.

Implementing Local Data Storage

Using Unity’s PlayerPrefs System

Unity makes local saving simple through the PlayerPrefs API which has methods like SetString, SetInt, and GetFloat. Data is stored locally in key-value pairs:


PlayerPrefs.SetString("PlayerName", "John");
PlayerPrefs.SetInt("HighScore", 48);

string name = PlayerPrefs.GetString("PlayerName");
int score = PlayerPrefs.GetInt("HighScore");

PlayerPrefs is best for small pieces of data like settings or high scores. Disk memory size ranges from 1 MB on mobile devices to 4 MB on desktop platforms for PlayerPrefs data.

Encrypting Sensitive Player Data

While local data storage can be useful for some data, sensitive information about a player’s account should utilize encryption prior to saving locally. This includes things like:

  • Usernames
  • Email addresses
  • Payment data

Sensitive data should be encrypted using cryptography libraries like OpenSSL before storage, and decrypted only temporarily in memory during gameplay sessions for security reasons.

Handling Large Amounts of Data

If large binary data files like textures or 3D models need saving, or structured data like inventories grow beyond tens of megabytes, local file storage becomes necessary due to PlayerPrefs constraints.

Binary and text files can be written directly to the local file system in specific folders. The full paths to files can then be saved in PlayerPrefs for lookup later.


// Get saved path from PlayerPrefs
string saveFilePath = PlayerPrefs.GetString("SaveFile"); 

// Load data from file
BinaryFormatter bf = new BinaryFormatter();
FileStream file = File.Open(saveFilePath, FileMode.Open);
SaveData data = (SaveData)bf.Deserialize(file);
file.Close();

// Write updates
FileStream fs = File.Create(saveFilePath); 
bf.Serialize(fs, data);
fs.Close();

This allows large data to be loaded and saved locally while persisting links to those files in PlayerPrefs across sessions.

Synchronizing Data to Remote Databases

Amazon Web Services, Firebase, PlayFab

There are a variety of backend-as-a-service providers for implementing remote data storage and synchronization in games. Popular options include:

  • Google Firebase – Mobile-focused with client SDKs for data synchronization
  • Amazon Web Services (AWS) – Robust cloud tools including DynamDB databases
  • PlayFab – Game-specific backends combined with social features

These providers reduce the need to build custom game server infrastructure. Many provide out-of-the-box support for data storage as well as features like player authentication, lobby systems, notifications, and cloud computing for game events.

Schemas for Common Game Data Types

Game data that needs synchronizing across devices can include:

  • Player profiles
  • Game state
  • Stats and achievements
  • Inventories or assets

This data can be organized using database schemas that structure related data into tables, objects, documents or graphs. Examples include:


PlayerProfile 
{
  username,
  emailAddress,
  passwordHash,
  dateRegistered,
  
  currentArea,
  health,
  items[]
}

Achievement
{
  name,
  description,  
  points,
  dateUnlocked  
} 

Well-designed schemas help model game data for saving and querying efficiency.

Handling Async Operations

Saving data to remote databases involves asynchronous operations which can take some time to complete. Game code needs to handle callbacks rather than blocking while uploads take place.


void SavePlayerData() {

  PlayerData data = GetPlayerData();
  
  remoteDB.UploadAsync(data, "players/123", (success) => {  
    if(success) {
      // Upload worked
    } else {
      // Handle error
    }
  });

}

Async operations prevent locking up the game while letting other game logic continue during the upload process.

Making Saved Games Cross-Platform

Abstracting the Saving System

To make a saving system work across multiple platforms like desktop, mobile, and consoles, the underlying implementation can be abstracted behind common interfaces:


public interface ISaveSystem
{
  void Save(SaveData data);
  SaveData Load(); 
}

public class PCSaveSystem : ISaveSystem
{
  public void Save(SaveData data) 
  {
    // Local file handling
  }
	
  public SaveData Load()
  { 
    // Load file data
  }
}

public class MobileSaveSystem : ISaveSystem
{
   // Remote implementation
}

Game code can then reference the interface while platform-specific saving details are encapsulated behind the interface.

Adapting Serialization Formats

The format for serialized data may differ across platforms. For example, player data could use JSON on mobile devices and ProtoBuffers on desktop platforms due to differences in efficiency and runtime support.


class CrossPlatformData
{
  // Properties common across platforms 
}

// Unity runtime serialization
[ProtoContract]
classPCDto : CrossPlatformData { }  

// JSON mobile serialization 
class MobileDto : CrossPlatformData { }

Mapping to a shared data contract class keeps the representation separate from the serialization format used on each target platform.

Streaming Large Amounts of Data

Local or remote uploads should avoid loading entire saved game files into memory. Stream reading and writing to files and databases keeps memory usage down.


FileStream saveFile = File.Open(path, FileMode.Open);

byte[] buffer = new byte[4096];
int bytesRead;

while((bytesRead = saveFile.Read(buffer, 0, buffer.Length)) > 0) 
{  
  // Write buffer to network stream
  networkStream.Write(buffer, 0, bytesRead);
}

saveFile.Close();

Streaming with decent buffer sizes allows large saved data to be uploaded without consuming too much memory.

Testing and Security Considerations

Simulating Loss of Connectivity

Game saving systems, especially those reliant on remote connectivity, should be tested with loss of signal during upload. Tests should confirm data does not become corrupted if connections drop mid-upload so coding mistakes are caught early.

Stress Testing Infrastructure

Load and stress testing helps evaluate backend infrastructure planned for production use. Large user volumes well beyond normal usage uncover weaknesses before customers are impacted.

Injecting delays, errors, and spikes in usage into test environments gives developers confidence in reliability metrics like uptime, data integrity, and scalability.

Securing Player Accounts and Data

Account security is crucial when profiles contain sensitive player data. Brute force and credential stuffing protections should be implemented along with mandatory post-login validation emails notices of suspicious activity.

All remote player data transmission should occur over encrypted SSL/TLS connections. Data at rest should also utilize encryption methods on the server side to protect sensitive fields.

Leave a Reply

Your email address will not be published. Required fields are marked *