Adding a save and load system to your game can greatly enhance the player experience. It allows players to persist their progress, resume gameplay sessions, and experiment with different strategies without losing hard-earned achievements.
You'll be pleasantly surprised by how straightforward it is to add this feature to your game using Python’s Arcade library.
Create a Simple Game
Start by creating a simple game where the player can move left and right.
The code used in this article is available in this GitHub repository and is free for you to use under the MIT license.
Create a new file named simple-game.py and add the below code:
import arcade
SCREEN_WIDTH = 800
SCREEN_HEIGHT = 600
PLAYER_SPEED = 5
blue = arcade.color.BLUE
class GameWindow(arcade.Window):
def __init__(self, width, height):
super().__init__(width, height)
self.player_x = width // 2
def on_draw(self):
arcade.start_render()
arcade.draw_rectangle_filled(self.game_state.player_x,
50, 50, 50, blue)
def update(self, delta_time):
pass
def on_key_press(self, key, modifiers):
if key == arcade.key.LEFT:
self.player_x -= PLAYER_SPEED
elif key == arcade.key.RIGHT:
self.player_x += PLAYER_SPEED
def main():
window = GameWindow(SCREEN_WIDTH, SCREEN_HEIGHT)
arcade.run()
if __name__ == '__main__':
main()
The code creates a window with a blue rectangle representing the player. The player can move left and right using the left and right arrow keys.
Managing Game States
To implement a save and load system, you need to manage different game states. A game state represents the current state of the game, including the positions of objects, scores, and other relevant data. For this example, focus on only the player's x-coordinate.
To manage game states, introduce a GameState class that encapsulates the game's data and provides methods to save and load it. Here's the code:
class GameState:
def __init__(self):
self.player_x = 0
Saving the Game Data
To save the game data, extend the GameWindow class and add a method to save the game state whenever necessary. Use the JSON format for simplicity. Here's the code:
In the save_game method, create a Python dictionary containing the relevant game data. Then serialize it into a JSON file called save.json.
import json
class GameWindow(arcade.Window):
def __init__(self, width, height):
super().__init__(width, height)
self.game_state = GameState()
def save_game(self):
data = {
'player_x': self.game_state.player_x
}
with open('save.json', 'w') as file:
json.dump(data, file)
print(data)
def on_draw(self):
arcade.start_render()
arcade.draw_rectangle_filled(self.game_state.player_x,
50, 50, 50, blue)
def update(self, delta_time):
pass
def on_key_press(self, key, modifiers):
if key == arcade.key.LEFT:
self.game_state.player_x -= PLAYER_SPEED
elif key == arcade.key.RIGHT:
self.game_state.player_x += PLAYER_SPEED
elif key == arcade.key.S:
self.save_game()
Loading the Game Data
To load the game data, extend the GameWindow class further and add a method to load the game state. Create a new file named load-game.py and add the code with the below updates:
class GameWindow(arcade.Window):
def __init__(self, width, height):
super().__init__(width, height)
self.game_state = GameState()
self.load_game()
def load_game(self):
try:
with open('save.json', 'r') as file:
data = json.load(file)
self.game_state.player_x = data['player_x']
except FileNotFoundError:
pass
def on_key_press(self, key, modifiers):
if key == arcade.key.L:
self.load_game()
The load_game method attempts to open the save.json file and retrieve the game data. It then updates the game state with the loaded data. If the file doesn't exist, you can just ignore the exception, leaving the default game state.
Including Additional Features
You can add more features to enhance the game’s save and load system.
Saving High Scores
Saving high scores alongside the game state is a common feature in many games. You can manage the scores and save the high score using this system. Create a new file named high-score.py and add the code with the below updates:
class GameWindow(arcade.Window):
def __init__(self, width, height):
super().__init__(width, height)
self.high_score = 0
def load_game(self):
try:
with open('save.json', 'r') as file:
data = json.load(file)
print(data)
self.player_x = data.get('player_x', self.player_x)
self.high_score = data.get('high_score', self.high_score)
except FileNotFoundError:
pass
def save_game(self):
data = {
'player_x': self.player_x,
'high_score': self.high_score
}
with open('save.json', 'w') as file:
json.dump(data, file)
print(data)
def on_key_press(self, key, modifiers):
if key == arcade.key.LEFT:
self.player_x -= PLAYER_SPEED
elif key == arcade.key.RIGHT:
self.player_x += PLAYER_SPEED
self.high_score += 1
Autosave Feature
To provide players with peace of mind and prevent loss of progress, you can automatically save the game state at regular intervals. Create a new file named auto-save.py and add the code with the below updates:
import time
class GameWindow(arcade.Window):
def __init__(self, width, height):
super().__init__(width, height)
self.game_state = GameState()
# Save every 6 seconds
self.autosave_interval = 6
self.last_save_time = time.time()
def update(self, delta_time):
current_time = time.time()
time_diff = current_time - self.last_save_time
if time_diff >= self.autosave_interval:
self.save_game()
print("Saved")
self.last_save_time = current_time
In this code snippet, the update method checks if the specified time interval has passed since the last save. If so, it triggers the save_game method of the GameWindow class to automatically save the game state. Adjust the autosave_interval value according to your game's requirements.
Validating Game Data
Validating the loaded game data is essential to ensure its integrity and consistency. You can easily incorporate data validation into our save and load system:
class GameState:
def __init__(self):
self.player_x = 0
def save_state(self):
if self.is_valid_state():
data = {
'player_x': self.player_x
}
with open('save.json', 'w') as file:
json.dump(data, file)
def load_state(self):
with open('save.json', 'r') as file:
data = json.load(file)
if self.validate_loaded_data(data):
self.player_x = data['player_x']
else:
print("Error!")
def is_valid_state(self):
# Perform validation logic here
# Return True if the state is valid, False otherwise
pass
def validate_loaded_data(self, data):
# Perform validation on the loaded data
# Return True if the data is valid, False otherwise
pass
By incorporating these additional features into the save and load system, you can create a more versatile and robust gameplay experience, offering players the ability to save multiple game states, track high scores, enable autosave, and ensure data integrity.
Best Practices for the Save and Load System
Implementing a save and load system is an important aspect of game development. To ensure a robust and reliable system, it's essential to follow best practices. Here are some key practices to consider:
Encrypt Sensitive Data
If your game includes sensitive information such as passwords, personal data, or in-app purchases, it's crucial to encrypt the saved game state. Encryption adds an extra layer of security, protecting the player's privacy and preventing unauthorized access to their data. Utilize encryption algorithms and libraries to safeguard sensitive information.
Validate Loaded Data
Before loading the game data, it's essential to validate it to ensure its integrity and consistency. Verify that the loaded data adheres to the expected format, structure, and constraints of your game.
Perform validation checks on critical data fields to avoid crashes or cheating. Implement robust data validation mechanisms to handle potential errors or unexpected data.
Handle Errors Gracefully
When dealing with file I/O operations, errors can occur. It's crucial to handle these errors gracefully and provide informative error messages to the player. Catch and handle exceptions, such as FileNotFoundError or PermissionError, during save and load operations.
Display user-friendly error messages to guide players and prevent frustration. Additionally, consider implementing error logging to help diagnose and fix issues.
Test Save and Load Functionality
Thoroughly test the save and load functionality of your game to ensure its reliability and correctness. Create test cases that cover various scenarios, such as saving in different game states, loading from valid and invalid save files, and testing edge cases.
Validate that the game state is properly saved and loaded and that the expected behavior occurs. Automated testing frameworks can assist in creating comprehensive test suites.
Make Games More Fun With Save and Load System
Adding a save and load system can make games more engaging by providing players with a sense of continuity and progress. Players can experiment freely, try different approaches, and return to the game later without losing their accomplishments.
This feature also allows players to compete with friends or challenge themselves to beat their previous high scores, adding replayability and long-term engagement to your game.